<?php
/**
 * @since   2022-02-20
 * @author  zhaoxiang <zhaoxiang051405@gmail.com>
 */

namespace App\Controller\Admin;

use App\Constants\DataType;
use App\Constants\ErrorCode;
use App\Middleware\AdminMiddleware;
use App\Model\AdminFields;
use App\Model\AdminList;
use App\Service\ToolService;
use Exception;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;
use Hyperf\HttpServer\Annotation\Middleware;
use Hyperf\HttpServer\Annotation\PostMapping;

#[Controller(prefix: '/admin/Fields'), Middleware(AdminMiddleware::class)]
class FieldsController extends BaseController {

    private array $dataType = [
        DataType::TYPE_INTEGER => 'Integer',
        DataType::TYPE_STRING  => 'String',
        DataType::TYPE_BOOLEAN => 'Boolean',
        DataType::TYPE_ENUM    => 'Enum',
        DataType::TYPE_FLOAT   => 'Float',
        DataType::TYPE_FILE    => 'File',
        DataType::TYPE_MOBILE  => 'Mobile',
        DataType::TYPE_OBJECT  => 'Object',
        DataType::TYPE_ARRAY   => 'Array'
    ];

    /**
     * @return array
     * @author zhaoxiang <zhaoxiang051405@gmail.com>
     */
    #[GetMapping(path: 'index')]
    public function index(): array {
        return $this->buildSuccess($this->dataType);
    }

    /**
     * 获取请求参数
     * @return array
     * @author zhaoxiang <zhaoxiang051405@gmail.com>
     */
    #[GetMapping(path: 'request')]
    public function request(): array {
        $limit = $this->request->query('size', config('apiadmin.admin_list_default'));
        $hash = $this->request->query('hash', '');

        if (empty($hash)) {
            return $this->buildFailed(ErrorCode::EMPTY_PARAMS, '缺少必要参数');
        }
        $listObj = AdminFields::query()->where('hash', $hash)->where('type', 0)
            ->paginate((int)$limit);
        $apiInfo = AdminList::query()->where('hash', $hash)->first();

        return $this->buildSuccess([
            'list'     => $listObj->items(),
            'count'    => $listObj->total(),
            'dataType' => $this->dataType,
            'apiInfo'  => $apiInfo->toArray()
        ]);
    }

    /**
     * 获取返回参数
     * @return array
     * @author zhaoxiang <zhaoxiang051405@gmail.com>
     */
    #[GetMapping(path: 'response')]
    public function response(): array {
        $limit = $this->request->query('size', config('apiadmin.admin_list_default'));
        $hash = $this->request->query('hash', '');

        if (empty($hash)) {
            return $this->buildFailed(ErrorCode::EMPTY_PARAMS, '缺少必要参数');
        }

        $listObj = AdminFields::query()->where('hash', $hash)->where('type', 1)
            ->paginate((int)$limit);
        $apiInfo = AdminList::query()->where('hash', $hash)->first();

        return $this->buildSuccess([
            'list'     => $listObj->items(),
            'count'    => $listObj->total(),
            'dataType' => $this->dataType,
            'apiInfo'  => $apiInfo->toArray()
        ]);
    }

    /**
     * 新增字段
     * @return array
     * @author zhaoxiang <zhaoxiang051405@gmail.com>
     */
    #[PostMapping(path: 'add')]
    public function add(): array {
        $postData = $this->request->post();
        $postData['show_name'] = $postData['field_name'];
        $postData['default'] = $postData['defaults'];
        unset($postData['defaults']);

        $postData = $this->filterData((new AdminFields())->getTable(), $postData);
        $res = AdminFields::insert($postData);
        $this->clearCache($postData['hash']);

        if ($res === false) {
            return $this->buildFailed(ErrorCode::DB_SAVE_ERROR);
        }

        return $this->buildSuccess();
    }

    /**
     * 字段编辑
     * @return array
     * @author zhaoxiang <zhaoxiang051405@gmail.com>
     */
    #[PostMapping(path: 'edit')]
    public function edit(): array {
        $postData = $this->request->post();
        $postData['show_name'] = $postData['field_name'];
        $postData['default'] = $postData['defaults'];
        unset($postData['defaults']);

        $postData = $this->filterData((new AdminFields())->getTable(), $postData);
        AdminFields::query()->where('id', $postData['id'])->update($postData);
        $this->clearCache($postData['hash']);

        return $this->buildSuccess();
    }

    /**
     * 字段删除
     * @return array
     * @throws Exception
     * @author zhaoxiang <zhaoxiang051405@gmail.com>
     */
    #[GetMapping(path: 'del')]
    public function del(): array {
        $id = $this->request->query('id');
        if (!$id) {
            return $this->buildFailed(ErrorCode::EMPTY_PARAMS, '缺少必要参数');
        }
        $fieldsInfo = AdminFields::query()->find($id);
        $this->clearCache($fieldsInfo->hash);
        $fieldsInfo->delete();

        return $this->buildSuccess();
    }

    /**
     * 批量上传返回字段
     * @return array
     * @author zhaoxiang <zhaoxiang051405@gmail.com>
     */
    #[PostMapping(path: 'upload')]
    public function upload(): array {
        $hash = $this->request->post('hash');
        $type = $this->request->post('type');

        $jsonStr = html_entity_decode($this->request->post('jsonStr'));
        $data = json_decode($jsonStr, true);
        if ($data === null) {
            return $this->buildFailed(ErrorCode::EXCEPTION, 'JSON数据格式有误');
        }
        AdminList::query()->where('hash', $hash)->update(['return_str' => json_encode($data)]);
        $this->handle($data['data'], $dataArr);
        $old = AdminFields::query()->where('hash', $hash)->where('type', $type)->get();
        $old = ToolService::buildArrFromObj($old);
        $oldArr = array_column($old, 'show_name');
        $newArr = array_column($dataArr, 'show_name');
        $addArr = array_diff($newArr, $oldArr);
        $delArr = array_diff($oldArr, $newArr);
        if ($delArr) {
            $delArr = array_values($delArr);
            AdminFields::query()->whereIn('show_name', $delArr)->delete();
        }
        if ($addArr) {
            $addData = [];
            foreach ($dataArr as $item) {
                if (in_array($item['show_name'], $addArr, true)) {
                    $addData[] = $item;
                }
            }
            AdminFields::insert($addData);
        }
        $this->clearCache($hash);

        return $this->buildSuccess();
    }

    /**
     * 更新数据缓存
     * @author zhaoxiang <zhaoxiang051405@gmail.com>
     */
    private function clearCache($hash): void {
//        cache('RequestFields:NewRule:' . $hash, null);
//        cache('RequestFields:Rule:' . $hash, null);
//        cache('ResponseFieldsRule:' . $hash, null);
    }

    /**
     * @param array $data
     * @param $dataArr
     * @param string $prefix
     * @param string $index
     * @author zhaoxiang <zhaoxiang051405@gmail.com>
     */
    private function handle(array $data, &$dataArr, string $prefix = 'data', string $index = 'data'): void {
        if (!$this->isAssoc($data)) {
            $addArr = [
                'field_name' => $index,
                'show_name'  => $prefix,
                'hash'       => $this->request->post('hash'),
                'is_must'    => 1,
                'data_type'  => DataType::TYPE_ARRAY,
                'type'       => $this->request->post('type')
            ];
            $dataArr[] = $addArr;
            $prefix .= '[]';
            if (isset($data[0]) && is_array($data[0])) {
                $this->handle($data[0], $dataArr, $prefix);
            }
        } else {
            $addArr = [
                'field_name' => $index,
                'show_name'  => $prefix,
                'hash'       => $this->request->post('hash'),
                'is_must'    => 1,
                'data_type'  => DataType::TYPE_OBJECT,
                'type'       => $this->request->post('type')
            ];
            $dataArr[] = $addArr;
            $prefix .= '{}';
            foreach ($data as $index => $datum) {
                $myPre = $prefix . $index;
                $addArr = array(
                    'field_name' => $index,
                    'show_name'  => $myPre,
                    'hash'       => $this->request->post('hash'),
                    'is_must'    => 1,
                    'data_type'  => DataType::TYPE_STRING,
                    'type'       => $this->request->post('type')
                );
                if (is_numeric($datum)) {
                    if (preg_match('/^\d*$/', $datum)) {
                        $addArr['data_type'] = DataType::TYPE_INTEGER;
                    } else {
                        $addArr['data_type'] = DataType::TYPE_FLOAT;
                    }
                    $dataArr[] = $addArr;
                } elseif (is_array($datum)) {
                    $this->handle($datum, $dataArr, $myPre, $index);
                } else {
                    $addArr['data_type'] = DataType::TYPE_STRING;
                    $dataArr[] = $addArr;
                }
            }
        }
    }

    /**
     * 判断是否是关联数组（true表示是关联数组）
     * @param array $arr
     * @return bool
     * @author zhaoxiang <zhaoxiang051405@gmail.com>
     */
    private function isAssoc(array $arr): bool {
        if (array() === $arr) {
            return false;
        }

        return array_keys($arr) !== range(0, count($arr) - 1);
    }
}
